package br.ufpb.di.ppgi.negocio.servicos;

import java.util.ArrayList;
import java.util.List;

import org.apache.commons.lang.RandomStringUtils;

import br.com.caelum.vraptor.Validator;
import br.com.caelum.vraptor.ioc.Component;
import br.com.caelum.vraptor.validator.Hibernate;
import br.com.caelum.vraptor.validator.ValidationMessage;
import br.ufpb.di.ppgi.apresentacao.SiteConfiguration;
import br.ufpb.di.ppgi.negocio.iservicos.IUsuarioService;
import br.ufpb.di.ppgi.negocio.modelo.contato.EmailImpl;
import br.ufpb.di.ppgi.negocio.modelo.contato.InternetIdentity;
import br.ufpb.di.ppgi.negocio.modelo.usuario.Usuario;
import br.ufpb.di.ppgi.negocio.modelo.usuario.UsuarioRole;
import br.ufpb.di.ppgi.negocio.modelo.usuario.UsuarioSessionInfo;
import br.ufpb.di.ppgi.negocio.servicos.login.Credencial;
import br.ufpb.di.ppgi.persistencia.domainstore.DomainStore;
import br.ufpb.di.ppgi.persistencia.iquery.IUsuarioQuery;
import br.ufpb.di.ppgi.persistencia.pagination.ListaPaginada;
import br.ufpb.di.ppgi.util.Mailer;
import br.ufpb.di.ppgi.util.PropertiesUtil;

@Component
public class UsuarioService implements IUsuarioService {
	private final DomainStore ds;
	private final UsuarioSessionInfo usuarioSessionInfo;
	private final Validator validator;
	private final IUsuarioQuery query;
	private final SiteConfiguration conf;
	private final PropertiesUtil properties;
	
	public UsuarioService(DomainStore ds, UsuarioSessionInfo usuarioSessionInfo, Validator validator, IUsuarioQuery query, SiteConfiguration conf, PropertiesUtil properties) {
		this.ds = ds;
		this.usuarioSessionInfo = usuarioSessionInfo;
		this.validator = validator;
		this.query = query;
		this.conf = conf;
		this.properties = properties;
	}
	
	@Override
	public Usuario buscar(Long id) {
		return ds.get(Usuario.class, id);
	}
	/**
	 * Altera a senha, de acordo com as credenciais, para o usuário logado, na sessão corrente.
	 * @param credential as novas crendenciais contendo a nova senha.
	 * @param nova 
	 * @return 
	 */
	@Override
	public boolean alterarSenha(Credencial antiga, Credencial nova) {
		if (!usuarioSessionInfo.isLogged())
			throw new IllegalStateException("Não há usuário logado para alteração da senha");
		if (!usuarioSessionInfo.confirmaSenha(antiga))
			return false;
		alterarSenha(nova,usuarioSessionInfo.getUsuario().getId());
		usuarioSessionInfo.getUsuario().setSenha(nova.getHashedPassword());
		return true;
	}
	/**
	 * Altera a senha para o usuário do <tt>id</tt> enviado.
	 * @param credential as novas crendenciais contendo a nova senha.
	 * @param id o ID do usuário o qual deseja-se alterar a senha.
	 */
	@Override
	public void alterarSenha(Credencial credential, Long id) {
		Usuario usuario = buscar(id);
		usuario.setSenha(credential.getHashedPassword());
	}
	
	@Override
	public List<Usuario> listar() {
		return query.listAll();
	}
	
	@Override
	public Usuario apagar(Long id) {
		if (usuarioSessionInfo.isLogged() && usuarioSessionInfo.getSessionId().equals(id)) {
			validator.add(new ValidationMessage("Não é possivel apagar o seu proprio usuário", "id"));
			return null;
		}
		return ds.remove(Usuario.class,id);
	}

	@Override
	public Usuario atualizar(Usuario usuario) {
		validator.addAll(Hibernate.validate(usuario));
		return validator.hasErrors() ? null : ds.merge(usuario);
		
	}

	@Override
	public void adicionar(Usuario usuario) {
		validator.addAll(Hibernate.validate(usuario));
		if (!validator.hasErrors()) {
			if (query.hasUsuarioByLogin(usuario.getEmail())) {
				validator.add(new ValidationMessage(
						"já existe um usuário com o login " + usuario.getEmail(),
						"login"));
				return;
			}
			ds.add(usuario);
		}
	}

	@Override
	public List<Usuario> listarProfessores() {
		List<UsuarioRole> roles = new ArrayList<UsuarioRole>();
		roles.add(UsuarioRole.COLEGIADO);
		roles.add(UsuarioRole.COORDENADOR);
		roles.add(UsuarioRole.PROFESSOR);
		
		return query.listByRoles(roles);
	}

	@Override
	public Integer qtdePendencias(Usuario colegiado) {
		return query.qtdePendencias(colegiado);
	}

	@Override
	public Integer qtdDefesasPendentes(Usuario colegiado) {
		return query.qtdDefesasPendentes(colegiado);
	}

	@Override
	public Integer qtdPropostasPendentes(Usuario colegiado) {
		return query.qtdPropostasPendentes(colegiado);
	}

	@Override
	public Usuario fetchCoordenador() {
		return query.fetchCoordenador();
	}

	@Override
	public Usuario findByEmail(String email) {
		return query.findByEmail(email);
	}
	
	@Override
	public Usuario findByCPF(String cpf) {
		return query.findByCPF(cpf);
	}

	@Override
	public void resetarSenhaEnviarEmail(Usuario usuario) {
		if (usuario == null || usuario.getId() == null)
			return;
		
		String senha = RandomStringUtils.randomAlphanumeric(8);
		usuario.setSenhaDescriptografada(senha);
		query.resetarSenha(usuario);
		
		String assunto = properties.getString("model.email.recuperarSenha.assunto");
		String mensagem = properties.getString("model.email.recuperarSenha.mensagem");
		
		EmailImpl email = new EmailImpl();
		email.setDe(conf.getDe());
		email.setPara(new InternetIdentity(usuario.getEmail(), usuario.getNome()));
		email.setAssunto(assunto);
		email.setMensagemEnviada(String.format(mensagem, senha));

		Mailer.getInstance().threadedSend(email);
	}

	@Override
	public ListaPaginada<Usuario> filtrarPaginando(String key, Integer page, String base) {
		return query.paginarByKey(key)
			.query(10, page)
			.setBase(base)
			.toVersaoEmPortugues();
	}

	@Override
	public boolean cpfJaExiste(String cpf) {
		return query.cpfExists(cpf);
	}
}
